home *** CD-ROM | disk | FTP | other *** search
- //----------------------------------------------------------------------------
- // File: DataRelay.cpp
- //
- // Desc: The main game file for the DataRelay sample. It connects
- // players together with two dialog boxes to prompt users on the
- // connection settings to join or create a session. After the user
- // connects to a sesssion, the sample displays a multiplayer stage.
- //
- // This sample uses DirectPlay to process high volumes of incoming
- // data on a seperate worker while avoiding copying the incoming packets
- // by using the ReturnBuffer() call. It also shows some basics of
- // thread synconization between the worker thread and the DirectPlay
- // message handler thread pool.
- //
- // Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
- //-----------------------------------------------------------------------------
- #define STRICT
- #include <windows.h>
- #include <basetsd.h>
- #include <process.h>
- #include <mmsystem.h>
- #include <dxerr8.h>
- #include <dplay8.h>
- #include <dplobby8.h>
- #include <stdio.h>
- #include <assert.h>
- #include "NetConnect.h"
- #include "DXUtil.h"
- #include "resource.h"
-
-
-
-
- //-----------------------------------------------------------------------------
- // Player context locking defines
- //-----------------------------------------------------------------------------
- CRITICAL_SECTION g_csPlayerContext;
- #define PLAYER_LOCK() EnterCriticalSection( &g_csPlayerContext );
- #define PLAYER_ADDREF( pPlayerInfo ) if( pPlayerInfo ) pPlayerInfo->lRefCount++;
- #define PLAYER_RELEASE( pPlayerInfo ) if( pPlayerInfo ) { pPlayerInfo->lRefCount--; if( pPlayerInfo->lRefCount <= 0 ) SAFE_DELETE( pPlayerInfo ); } pPlayerInfo = NULL;
- #define PLAYER_UNLOCK() LeaveCriticalSection( &g_csPlayerContext );
-
-
- //-----------------------------------------------------------------------------
- // Defines, and constants
- //-----------------------------------------------------------------------------
- #define DPLAY_SAMPLE_KEY TEXT("Software\\Microsoft\\DirectX DirectPlay Samples")
- #define MAX_PLAYER_NAME 14
- #define TIMERID_NETWORK 0
- #define TIMERID_STATS 1
- #define WM_APP_UPDATE_TARGETS (WM_APP + 0)
- #define WM_APP_APPEND_TEXT (WM_APP + 1)
-
- // This GUID allows DirectPlay to find other instances of the same game on
- // the network. So it must be unique for every game, and the same for
- // every instance of that game. // {BA214178-AAE6-4ea6-84E0-65CE36F84479}
- GUID g_guidApp = { 0xba214178, 0xaae6, 0x4ea6, { 0x84, 0xe0, 0x65, 0xce, 0x36, 0xf8, 0x44, 0x79 } };
-
- struct APP_PLAYER_INFO
- {
- LONG lRefCount; // Ref count so we can cleanup when all threads
- // are done w/ this object
- DPNID dpnidPlayer; // DPNID of player
- TCHAR strPlayerName[MAX_PLAYER_NAME]; // Player name
- DWORD dwFlags; // Player flags
- APP_PLAYER_INFO* pNext;
- APP_PLAYER_INFO* pPrev;
- };
-
-
-
-
- //-----------------------------------------------------------------------------
- // App specific DirectPlay messages and structures
- //-----------------------------------------------------------------------------
- #define GAME_MSGID_GAMEPACKET 1
-
- // Change compiler pack alignment to be BYTE aligned, and pop the current value
- #pragma pack( push, 1 )
-
- struct GAMEMSG_GENERIC
- {
- DWORD dwType;
- DWORD dwPacketId;
- };
-
- #define GAMEMSG_GENERIC_SIZE 8
-
- struct GAMEMSG_DATA_512 : GAMEMSG_GENERIC
- {
- BYTE pBuffer[512-GAMEMSG_GENERIC_SIZE];
- };
-
- struct GAMEMSG_DATA_256 : GAMEMSG_GENERIC
- {
- BYTE pBuffer[256-GAMEMSG_GENERIC_SIZE];
- };
-
- struct GAMEMSG_DATA_128 : GAMEMSG_GENERIC
- {
- BYTE pBuffer[128-GAMEMSG_GENERIC_SIZE];
- };
-
- struct GAMEMSG_DATA_64 : GAMEMSG_GENERIC
- {
- BYTE pBuffer[64-GAMEMSG_GENERIC_SIZE];
- };
-
- struct GAMEMSG_DATA_32 : GAMEMSG_GENERIC
- {
- BYTE pBuffer[32-GAMEMSG_GENERIC_SIZE];
- };
-
- struct GAMEMSG_DATA_16 : GAMEMSG_GENERIC
- {
- BYTE pBuffer[16-GAMEMSG_GENERIC_SIZE];
- };
-
- #define DATA_TYPE_NETPACKET_RECIEVE 1
- #define DATA_TYPE_NETPACKET_SENT 2
- #define DATA_TYPE_NETPACKET_TIMEOUT 3
-
- struct GAMEMSG_DATA_NODE
- {
- GAMEMSG_GENERIC* pDataMsg;
- DWORD dwPacketId;
- APP_PLAYER_INFO* pPlayerFrom;
- DWORD dwReceiveDataSize;
- DPNHANDLE hBufferHandle;
- DWORD dwType;
- GAMEMSG_DATA_NODE* pNext;
- GAMEMSG_DATA_NODE* pPrev;
- };
-
- // Pop the old pack alignment
- #pragma pack( pop )
-
-
-
-
- //-----------------------------------------------------------------------------
- // Global variables
- //-----------------------------------------------------------------------------
- TCHAR g_strAppName[256] = TEXT("DataRelay");
- IDirectPlay8Peer* g_pDP = NULL; // DirectPlay peer object
- CNetConnectWizard* g_pNetConnectWizard = NULL; // Connection wizard
- IDirectPlay8LobbiedApplication* g_pLobbiedApp = NULL; // DirectPlay lobbied app
- BOOL g_bWasLobbyLaunched = FALSE; // TRUE if lobby launched
- HINSTANCE g_hInst = NULL; // HINST of app
- HWND g_hDlg = NULL; // HWND of main dialog
- HRESULT g_hrDialog; // Exit code for app
- TCHAR g_strLocalPlayerName[MAX_PATH]; // Local player name
- TCHAR g_strSessionName[MAX_PATH]; // Session name
- TCHAR g_strPreferredProvider[MAX_PATH]; // Provider string
- HANDLE g_hDPDataAvailEvent = NULL; // Signaled if there is data to process
- HANDLE g_hShutdownEvent = NULL; // Signaled if shutting down
- DWORD g_dwPacketId = 0; // Packet id counter
- APP_PLAYER_INFO* g_pTargetPlayer = NULL; // The current target player
- BOOL g_bSendingData = FALSE; // TRUE if sending data
- DPNID g_dpnidLocalPlayer = 0; // DPNID of local player
- DWORD g_dwDataRecieved = 0; // Amount of data recieved
- DWORD g_dwDataSent = 0; // Amount of data sent
- DWORD g_dwSendTimeout = 0; // Length of send timeout
- DWORD g_dwTimeBetweenSends = 0; // Time between packet sends
- DWORD g_dwSendSize = 0; // Size in bytes of packet
- UINT g_dwProcessNetDataThreadID = 0; // Worker thread ID
- HANDLE g_hProcessNetDataThread = NULL; // Worker thread handle
- APP_PLAYER_INFO g_PlayerHead; // Linked list of players connected
- GAMEMSG_DATA_NODE g_DataHead; // Linked list of data to process
- CRITICAL_SECTION g_csPlayerList; // CS for g_PlayerHead
- CRITICAL_SECTION g_csDataList; // CS for g_DataHead
- APP_PLAYER_INFO* g_pConnInfoTargetPlayer = NULL; // The current target for Connection Info
-
-
-
-
- //-----------------------------------------------------------------------------
- // Function-prototypes
- //-----------------------------------------------------------------------------
- HRESULT WINAPI DirectPlayMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
- HRESULT WINAPI DirectPlayLobbyMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
- INT_PTR CALLBACK SampleDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
- UINT WINAPI ProcessNetDataProc( LPVOID lpParameter );
- HRESULT InitDirectPlay();
- HRESULT OnInitDialog( HWND hDlg );
- VOID FillTargetCombo( HWND hDlg );
- VOID FillOneTimeCombos( HWND hDlg );
- HRESULT LinkPlayer( DPNID dpnid, APP_PLAYER_INFO** ppPlayerInfo );
- HRESULT SendNetworkData();
- HRESULT ProcessData();
- VOID AppendTextToEditControl( HWND hDlg, TCHAR* strNewLogLine );
- VOID ReadCombos( HWND hDlg );
- VOID UpdateSendQueueInfo( HWND hDlg );
- VOID UpdateStats( HWND hDlg );
-
-
-
-
- //-----------------------------------------------------------------------------
- // Name: WinMain()
- // Desc: Entry point for the application. Since we use a simple dialog for
- // user interaction we don't need to pump messages.
- //-----------------------------------------------------------------------------
- INT APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInst,
- LPSTR pCmdLine, INT nCmdShow )
- {
- HRESULT hr;
- BOOL bConnectSuccess = FALSE;
- HKEY hDPlaySampleRegKey;
-
- // Create events and critical sections
- g_hDPDataAvailEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
- g_hShutdownEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
- InitializeCriticalSection( &g_csPlayerList );
- InitializeCriticalSection( &g_csDataList );
- InitializeCriticalSection( &g_csPlayerContext );
-
- g_hInst = hInst;
-
- // Init circular linked list
- ZeroMemory( &g_PlayerHead, sizeof(APP_PLAYER_INFO) );
- g_PlayerHead.pNext = &g_PlayerHead;
- g_PlayerHead.pPrev = &g_PlayerHead;
- g_PlayerHead.dpnidPlayer = DPNID_ALL_PLAYERS_GROUP;
- _tcscpy( g_PlayerHead.strPlayerName, TEXT("Everyone") );
-
- // Init circular linked list
- ZeroMemory( &g_DataHead, sizeof(GAMEMSG_DATA_NODE) );
- g_DataHead.pNext = &g_DataHead;
- g_DataHead.pPrev = &g_DataHead;
-
- // Read persistent state information from registry
- RegCreateKeyEx( HKEY_CURRENT_USER, DPLAY_SAMPLE_KEY, 0, NULL,
- REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
- &hDPlaySampleRegKey, NULL );
- DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Player Name"),
- g_strLocalPlayerName, MAX_PATH, TEXT("TestPlayer") );
- DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Session Name"),
- g_strSessionName, MAX_PATH, TEXT("TestGame") );
- DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Preferred Provider"),
- g_strPreferredProvider, MAX_PATH, TEXT("DirectPlay8 TCP/IP Service Provider") );
-
- // Init COM so we can use CoCreateInstance
- CoInitializeEx( NULL, COINIT_MULTITHREADED );
-
- // Create helper class
- g_pNetConnectWizard = new CNetConnectWizard( hInst, NULL, g_strAppName, &g_guidApp );
-
- if( FAILED( hr = InitDirectPlay() ) )
- {
- DXTRACE_ERR( TEXT("InitDirectPlay"), hr );
- MessageBox( NULL, TEXT("Failed initializing IDirectPlay8Peer. ")
- TEXT("The sample will now quit."),
- TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
- return FALSE;
- }
-
- // Check if we were launched from a lobby client
- if( g_bWasLobbyLaunched && g_pNetConnectWizard->HaveConnectionSettingsFromLobby() )
- {
- // If were lobby launched then DPL_MSGID_CONNECT has already been
- // handled, so we can just tell the wizard to connect to the lobby
- // that has sent us a DPL_MSGID_CONNECT msg.
- if( FAILED( hr = g_pNetConnectWizard->ConnectUsingLobbySettings() ) )
- {
- DXTRACE_ERR( TEXT("ConnectUsingLobbySettings"), hr );
- MessageBox( NULL, TEXT("Failed to connect using lobby settings. ")
- TEXT("The sample will now quit."),
- TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
-
- bConnectSuccess = FALSE;
- }
- else
- {
- // Read information from g_pNetConnectWizard
- _tcsncpy( g_strLocalPlayerName, g_pNetConnectWizard->GetPlayerName(), MAX_PLAYER_NAME );
- g_strLocalPlayerName[MAX_PLAYER_NAME-1]=0;
-
- bConnectSuccess = TRUE;
- }
- }
- else
- {
- // If not lobby launched, prompt the user about the network
- // connection and which session they would like to join or
- // if they want to create a new one.
-
- // Setup connection wizard
- g_pNetConnectWizard->SetPlayerName( g_strLocalPlayerName );
- g_pNetConnectWizard->SetSessionName( g_strSessionName );
- g_pNetConnectWizard->SetPreferredProvider( g_strPreferredProvider );
-
- // Do the connection wizard
- hr = g_pNetConnectWizard->DoConnectWizard( FALSE );
- if( FAILED( hr ) )
- {
- DXTRACE_ERR( TEXT("DoConnectWizard"), hr );
- MessageBox( NULL, TEXT("Multiplayer connect failed. ")
- TEXT("The sample will now quit."),
- TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
- bConnectSuccess = FALSE;
- }
- else if( hr == NCW_S_QUIT )
- {
- // The user canceled the Multiplayer connect, so quit
- bConnectSuccess = FALSE;
- }
- else
- {
- bConnectSuccess = TRUE;
-
- // Read information from g_pNetConnectWizard
- _tcsncpy( g_strLocalPlayerName, g_pNetConnectWizard->GetPlayerName(), MAX_PLAYER_NAME );
- g_strLocalPlayerName[MAX_PLAYER_NAME-1]=0;
- _tcscpy( g_strSessionName, g_pNetConnectWizard->GetSessionName() );
- _tcscpy( g_strPreferredProvider, g_pNetConnectWizard->GetPreferredProvider() );
-
- // Write information to the registry
- DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Player Name"), g_strLocalPlayerName );
- DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Session Name"), g_strSessionName );
- DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Preferred Provider"), g_strPreferredProvider );
- }
- }
-
- if( bConnectSuccess )
- {
- // For this sample, we just start a simple dialog box game.
- g_hrDialog = S_OK;
- DialogBox( hInst, MAKEINTRESOURCE(IDD_MAIN_GAME), NULL,
- (DLGPROC) SampleDlgProc );
- }
-
- // Close down process data thread
- SetEvent( g_hShutdownEvent );
- WaitForSingleObject( g_hProcessNetDataThread, INFINITE );
- CloseHandle( g_hProcessNetDataThread );
-
- if( FAILED( g_hrDialog ) )
- {
- if( g_hrDialog == DPNERR_CONNECTIONLOST )
- {
- MessageBox( NULL, TEXT("The DirectPlay session was lost. ")
- TEXT("The sample will now quit."),
- TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
- }
- else
- {
- DXTRACE_ERR( TEXT("DialogBox"), g_hrDialog );
- MessageBox( NULL, TEXT("An error occured during the game. ")
- TEXT("The sample will now quit."),
- TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
- }
- }
-
- // Cleanup DirectPlay and helper classes
- g_pNetConnectWizard->Shutdown();
-
- if( g_pDP )
- {
- g_pDP->Close(0);
- SAFE_RELEASE( g_pDP );
- }
-
- if( g_pLobbiedApp )
- {
- g_pLobbiedApp->Close( 0 );
- SAFE_RELEASE( g_pLobbiedApp );
- }
-
- // Don't delete the wizard until we know that
- // DirectPlay is out of its message handlers.
- // This will be true after Close() has been called.
- SAFE_DELETE( g_pNetConnectWizard );
-
- // Cleanup circular linked list, g_PlayerHead
- {
- EnterCriticalSection( &g_csPlayerList );
- APP_PLAYER_INFO* pNode = g_PlayerHead.pNext;
- APP_PLAYER_INFO* pDeleteNode;
- while( pNode != &g_PlayerHead )
- {
- pDeleteNode = pNode;
- pNode = pNode->pNext;
- SAFE_DELETE( pDeleteNode );
- }
- LeaveCriticalSection( &g_csPlayerList );
- }
-
- // Cleanup circular linked list, g_DataHead
- {
- EnterCriticalSection( &g_csDataList );
- GAMEMSG_DATA_NODE* pNode = g_DataHead.pNext;
- GAMEMSG_DATA_NODE* pDeleteNode;
- while( pNode != &g_DataHead )
- {
- pDeleteNode = pNode;
- pNode = pNode->pNext;
- SAFE_DELETE( pDeleteNode );
- }
- LeaveCriticalSection( &g_csDataList );
- }
-
- // Cleanup Win32 resources
- RegCloseKey( hDPlaySampleRegKey );
- CloseHandle( g_hShutdownEvent );
- CloseHandle( g_hDPDataAvailEvent );
- DeleteCriticalSection( &g_csPlayerList );
- DeleteCriticalSection( &g_csDataList );
- DeleteCriticalSection( &g_csPlayerContext );
- CoUninitialize();
-
- return TRUE;
- }
-
-
-
-
- //-----------------------------------------------------------------------------
- // Name: InitDirectPlay()
- // Desc:
- //-----------------------------------------------------------------------------
- HRESULT InitDirectPlay()
- {
- DPNHANDLE hLobbyLaunchedConnection = NULL;
- HRESULT hr;
-
- // Create IDirectPlay8Peer
- if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Peer, NULL,
- CLSCTX_INPROC_SERVER,
- IID_IDirectPlay8Peer,
- (LPVOID*) &g_pDP ) ) )
- return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
-
- // Create IDirectPlay8LobbiedApplication
- if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8LobbiedApplication, NULL,
- CLSCTX_INPROC_SERVER,
- IID_IDirectPlay8LobbiedApplication,
- (LPVOID*) &g_pLobbiedApp ) ) )
- return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
-
- // Init the helper class, now that g_pDP and g_pLobbiedApp are valid
- g_pNetConnectWizard->Init( g_pDP, g_pLobbiedApp );
-
- // Init IDirectPlay8Peer
- if( FAILED( hr = g_pDP->Initialize( NULL, DirectPlayMessageHandler, 0 ) ) )
- return DXTRACE_ERR( TEXT("Initialize"), hr );
-
- // Init IDirectPlay8LobbiedApplication. Before this Initialize() returns
- // a DPL_MSGID_CONNECT msg may come in to the DirectPlayLobbyMessageHandler
- // so be prepared ahead of time.
- if( FAILED( hr = g_pLobbiedApp->Initialize( NULL, DirectPlayLobbyMessageHandler,
- &hLobbyLaunchedConnection, 0 ) ) )
- return DXTRACE_ERR( TEXT("Initialize"), hr );
-
- // IDirectPlay8LobbiedApplication::Initialize returns a handle to a connnection
- // if we have been lobby launced. Initialize is guanteeded to return after
- // the DPL_MSGID_CONNECT msg has been processed. So unless a we are expected
- // multiple lobby connections, we do not need to remember the lobby connection
- // handle since it will be recorded upon the DPL_MSGID_CONNECT msg.
- g_bWasLobbyLaunched = ( hLobbyLaunchedConnection != NULL );
-
- return S_OK;
- }
-
-
-
-
- //-----------------------------------------------------------------------------
- // Name: SampleDlgProc()
- // Desc: Handles dialog messages
- //-----------------------------------------------------------------------------
- INT_PTR CALLBACK SampleDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
- {
- switch( msg )
- {
- case WM_INITDIALOG:
- {
- g_hDlg = hDlg;
- if( FAILED( g_hrDialog = OnInitDialog( hDlg ) ) )
- {
- DXTRACE_ERR( TEXT("OnInitDialog"), g_hrDialog );
- EndDialog( hDlg, 0 );
- }
-
- break;
- }
-
- case WM_APP_UPDATE_TARGETS:
- {
- // Display the player names in the UI
- CheckDlgButton( hDlg, IDC_SEND_READY, (g_bSendingData) ? BST_CHECKED : BST_UNCHECKED );
- PostMessage( hDlg, WM_COMMAND, IDC_SEND_READY, 0 );
- FillTargetCombo( g_hDlg );
- ReadCombos( g_hDlg );
- break;
- }
-
- case WM_APP_APPEND_TEXT:
- {
- // Append a string to the edit control
- TCHAR* strNewLogLine = (TCHAR*) wParam;
- AppendTextToEditControl( g_hDlg, strNewLogLine );
- SAFE_DELETE( strNewLogLine );
- break;
- }
-
- case WM_COMMAND:
- {
- switch( LOWORD(wParam) )
- {
- case IDC_SEND_READY:
- g_bSendingData = (IsDlgButtonChecked( hDlg, IDC_SEND_READY ) == BST_CHECKED );
- if( g_bSendingData )
- {
- SetTimer( g_hDlg, TIMERID_NETWORK, g_dwTimeBetweenSends, NULL );
- SetDlgItemText( hDlg, IDC_SEND_READY, TEXT("Sending...") );
- }
- else
- {
- KillTimer( g_hDlg, TIMERID_NETWORK );
- SetDlgItemText( hDlg, IDC_SEND_READY, TEXT("Push to Send") );
- }
- break;
-
- case IDCANCEL:
- g_hrDialog = S_OK;
- EndDialog( hDlg, 0 );
- return TRUE;
-
- case IDC_SEND_SIZE_COMBO:
- case IDC_SEND_RATE_COMBO:
- case IDC_SEND_TARGET_COMBO:
- case IDC_CONNINFO_COMBO:
- if( HIWORD(wParam) == CBN_SELENDOK )
- ReadCombos( hDlg );
- break;
- }
- break;
- }
-
- case WM_TIMER:
- {
- switch( wParam )
- {
- case TIMERID_NETWORK:
- {
- // Send network data
- if( FAILED( g_hrDialog = SendNetworkData() ) )
- {
- DXTRACE_ERR( TEXT("SendNetworkData"), g_hrDialog );
- EndDialog( hDlg, 0 );
- }
- break;
- }
-
- case TIMERID_STATS:
- {
- UpdateStats( hDlg );
- break;
- }
- }
- break;
- }
- }
-
- return FALSE; // Didn't handle message
- }
-
-
-
-
- //-----------------------------------------------------------------------------
- // Name: OnInitDialog()
- // Desc: Inits the dialog.
- //-----------------------------------------------------------------------------
- HRESULT OnInitDialog( HWND hDlg )
- {
- // Load and set the icon
- HICON hIcon = LoadIcon( g_hInst, MAKEINTRESOURCE( IDI_MAIN ) );
- SendMessage( hDlg, WM_SETICON, ICON_BIG, (LPARAM) hIcon ); // Set big icon
- SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon ); // Set small icon
-
- // Display local player's name
- SetDlgItemText( hDlg, IDC_PLAYER_NAME, g_strLocalPlayerName );
-
- // Start a thread to process the network data
- g_hProcessNetDataThread = (HANDLE) _beginthreadex( NULL, 0, ProcessNetDataProc,
- hDlg, 0, &g_dwProcessNetDataThreadID );
-
- // Update the dialog box
- SendMessage( hDlg, WM_APP_UPDATE_TARGETS, 0, 0 );
- FillOneTimeCombos( hDlg );
- ReadCombos( hDlg );
-
- // Update the stats every second
- SetTimer( hDlg, TIMERID_STATS, 1000, NULL );
-
- if( g_pNetConnectWizard->IsHostPlayer() )
- SetWindowText( hDlg, TEXT("DataRelay (Host)") );
- else
- SetWindowText( hDlg, TEXT("DataRelay") );
-
- return S_OK;
- }
-
-
-
-
- //-----------------------------------------------------------------------------
- // Name: FillTargetCombo()
- // Desc: Fills the target combo with all of the players
- //-----------------------------------------------------------------------------
- VOID FillTargetCombo( HWND hDlg )
- {
- DWORD dwNumberOfActivePlayers = 0;
- int nIndex;
- int nCurSelect;
- int nNewSelect = 0;
- LONG_PTR lCurItemData;
- HWND hTargetCombo;
- HWND hInfoTargetCombo;
-
- if( hDlg == NULL )
- return;
-
- hTargetCombo = GetDlgItem( hDlg, IDC_SEND_TARGET_COMBO );
- if( hTargetCombo == NULL )
- return;
-
- nCurSelect = (int)SendMessage( hTargetCombo, CB_GETCURSEL, 0, 0 );
- lCurItemData = (LONG_PTR)SendMessage( hTargetCombo, CB_GETITEMDATA, nCurSelect, 0 );
-
- // Clear combo box
- SendMessage( hTargetCombo, CB_RESETCONTENT, 0, 0 );
-
- // Add "everyone"
- nIndex = (int)SendMessage( hTargetCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("Everyone") );
- SendMessage( hTargetCombo, CB_SETITEMDATA, nIndex, (LPARAM) &g_PlayerHead );
-
- // Enter the player list CS, because we are about to read shared memory, and
- // the data may be changed concurrently from the DirectPlay
- // message handler threads
- EnterCriticalSection( &g_csPlayerList );
-
- // Add each player
- APP_PLAYER_INFO* pNode = g_PlayerHead.pNext;
- while( pNode != &g_PlayerHead )
- {
- if( (pNode->dwFlags & DPNPLAYER_LOCAL) == 0 )
- {
- nIndex = (int)SendMessage( hTargetCombo, CB_ADDSTRING, 0, (LPARAM) pNode->strPlayerName );
- SendMessage( hTargetCombo, CB_SETITEMDATA, nIndex, (LPARAM) pNode );
- if( lCurItemData == (LONG_PTR) pNode )
- nNewSelect = nIndex;
- }
-
- dwNumberOfActivePlayers++;
- pNode = pNode->pNext;
- }
-
- LeaveCriticalSection( &g_csPlayerList );
-
- SendMessage( hTargetCombo, CB_SETCURSEL, nNewSelect, 0 );
-
- // Update the number of players in game counter
- TCHAR strNumberPlayers[32];
- wsprintf( strNumberPlayers, TEXT("%d"), dwNumberOfActivePlayers );
- SetDlgItemText( hDlg, IDC_NUM_PLAYERS, strNumberPlayers );
-
- if( dwNumberOfActivePlayers > 1 )
- {
- EnableWindow( GetDlgItem( hDlg, IDC_SEND_READY ), TRUE );
- }
- else
- {
- EnableWindow( GetDlgItem( hDlg, IDC_SEND_READY ), FALSE );
- CheckDlgButton( hDlg, IDC_SEND_READY, BST_UNCHECKED );
- SetDlgItemText( hDlg, IDC_SEND_READY, TEXT("Push to Send") );
- }
-
- // Populate the connection info box
- hInfoTargetCombo = GetDlgItem( hDlg, IDC_CONNINFO_COMBO );
-
- nCurSelect = (int)SendMessage( hInfoTargetCombo, CB_GETCURSEL, 0, 0 );
- lCurItemData = (LONG_PTR)SendMessage( hInfoTargetCombo, CB_GETITEMDATA, nCurSelect, 0 );
-
- // Clear combo box
- SendMessage( hInfoTargetCombo, CB_RESETCONTENT, 0, 0 );
-
- // Add "none"
- nIndex = (int)SendMessage( hInfoTargetCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("None") );
- SendMessage( hInfoTargetCombo, CB_SETITEMDATA, nIndex, (LPARAM) &g_PlayerHead );
-
- // Enter the player list CS, because we are about to read shared memory, and
- // the data may be changed concurrently from the DirectPlay
- // message handler threads
- EnterCriticalSection( &g_csPlayerList );
-
- // Add each player
- pNode = g_PlayerHead.pNext;
- nNewSelect = 0;
- while( pNode != &g_PlayerHead )
- {
- if( (pNode->dwFlags & DPNPLAYER_LOCAL) == 0 )
- {
- nIndex = (int)SendMessage( hInfoTargetCombo, CB_ADDSTRING, 0, (LPARAM) pNode->strPlayerName );
- SendMessage( hInfoTargetCombo, CB_SETITEMDATA, nIndex, (LPARAM) pNode );
- if( lCurItemData == (LONG_PTR) pNode )
- nNewSelect = nIndex;
- }
-
- dwNumberOfActivePlayers++;
- pNode = pNode->pNext;
- }
-
- LeaveCriticalSection( &g_csPlayerList );
-
- SendMessage( hInfoTargetCombo, CB_SETCURSEL, nNewSelect, 0 );
- }
-
-
-
-
- //-----------------------------------------------------------------------------
- // Name: FillOneTimeCombos()
- // Desc: Fill the unchanging UI combos box
- //-----------------------------------------------------------------------------
- VOID FillOneTimeCombos( HWND hDlg )
- {
- if( hDlg == NULL )
- return;
-
- HWND hRateCombo = GetDlgItem( hDlg, IDC_SEND_RATE_COMBO );
- SendMessage( hRateCombo, WM_SETREDRAW, FALSE, 0 );
- SendMessage( hRateCombo, CB_RESETCONTENT, 0, 0 );
- SendMessage( hRateCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("1000") );
- SendMessage( hRateCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("500") );
- SendMessage( hRateCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("250") );
- SendMessage( hRateCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("100") );
- SendMessage( hRateCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("50") );
- SendMessage( hRateCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("0") );
- SendMessage( hRateCombo, WM_SETREDRAW, TRUE, 0 );
- SendMessage( hRateCombo, CB_SETCURSEL, 0, 0 );
-
- HWND hSizeCombo = GetDlgItem( hDlg, IDC_SEND_SIZE_COMBO );
- SendMessage( hSizeCombo, WM_SETREDRAW, FALSE, 0 );
- SendMessage( hSizeCombo, CB_RESETCONTENT, 0, 0 );
- SendMessage( hSizeCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("512") );
- SendMessage( hSizeCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("256") );
- SendMessage( hSizeCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("128") );
- SendMessage( hSizeCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("64") );
- SendMessage( hSizeCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("32") );
- SendMessage( hSizeCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("16") );
- SendMessage( hSizeCombo, WM_SETREDRAW, TRUE, 0 );
- SendMessage( hSizeCombo, CB_SETCURSEL, 0, 0 );
-
- HWND hTimeoutCombo = GetDlgItem( hDlg, IDC_SEND_TIMEOUT_COMBO );
- SendMessage( hTimeoutCombo, WM_SETREDRAW, FALSE, 0 );
- SendMessage( hTimeoutCombo, CB_RESETCONTENT, 0, 0 );
- SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("5") );
- SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("10") );
- SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("20") );
- SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("50") );
- SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("100") );
- SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("250") );
- SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("500") );
- SendMessage( hTimeoutCombo, WM_SETREDRAW, TRUE, 0 );
- SendMessage( hTimeoutCombo, CB_SETCURSEL, 2, 0 );
- }
-
-
-
-
- //-----------------------------------------------------------------------------
- // Name: ReadCombos()
- // Desc: Reads the state of the combos and updates the global vars
- //-----------------------------------------------------------------------------
- VOID ReadCombos( HWND hDlg )
- {
- TCHAR strText[128];
- int nCurSelect;
-
- // Get target player
- HWND hTargetCombo = GetDlgItem( hDlg, IDC_SEND_TARGET_COMBO );
- nCurSelect = (int)SendMessage( hTargetCombo, CB_GETCURSEL, 0, 0 );
- if( nCurSelect != CB_ERR )
- {
- g_pTargetPlayer = (APP_PLAYER_INFO*) SendMessage( hTargetCombo, CB_GETITEMDATA,
- nCurSelect, 0 );
- }
-
- // Get rate (in ms)
- HWND hRateCombo = GetDlgItem( hDlg, IDC_SEND_RATE_COMBO );
- nCurSelect = (int)SendMessage( hRateCombo, CB_GETCURSEL, 0, 0 );
- if( nCurSelect != CB_ERR )
- {
- SendMessage( hRateCombo, CB_GETLBTEXT, nCurSelect, (LPARAM) strText );
- g_dwTimeBetweenSends = _ttoi( strText );
- KillTimer( g_hDlg, TIMERID_NETWORK );
- SetTimer( g_hDlg, TIMERID_NETWORK, g_dwTimeBetweenSends, NULL );
- }
-
- // Get size (in bytes)
- HWND hSizeCombo = GetDlgItem( hDlg, IDC_SEND_SIZE_COMBO );
- nCurSelect = (int)SendMessage( hSizeCombo, CB_GETCURSEL, 0, 0 );
- if( nCurSelect != CB_ERR )
- {
- SendMessage( hSizeCombo, CB_GETLBTEXT, nCurSelect, (LPARAM) strText );
- g_dwSendSize = _ttoi( strText );
- }
-
- // Get timeout (in ms)
- HWND hTimeoutCombo = GetDlgItem( hDlg, IDC_SEND_TIMEOUT_COMBO );
- nCurSelect = (int)SendMessage( hTimeoutCombo, CB_GETCURSEL, 0, 0 );
- if( nCurSelect != CB_ERR )
- {
- SendMessage( hTimeoutCombo, CB_GETLBTEXT, nCurSelect, (LPARAM) strText );
- g_dwSendTimeout = _ttoi( strText );
- }
-
- // Get the ConnectionInfo Target player
- HWND hConnTargetCombo = GetDlgItem( hDlg, IDC_CONNINFO_COMBO );
- nCurSelect = (int)SendMessage( hConnTargetCombo, CB_GETCURSEL, 0, 0 );
- if( nCurSelect != CB_ERR )
- {
- g_pConnInfoTargetPlayer = (APP_PLAYER_INFO*) SendMessage( hConnTargetCombo, CB_GETITEMDATA,
- nCurSelect, 0 );
- }
- }
-
-
-
-
- //-----------------------------------------------------------------------------
- // Name: DirectPlayMessageHandler
- // Desc: Handler for DirectPlay messages. This function is called by
- // the DirectPlay message handler pool of threads, so be care of thread
- // synchronization problems with shared memory
- //-----------------------------------------------------------------------------
- HRESULT WINAPI DirectPlayMessageHandler( PVOID pvUserContext,
- DWORD dwMessageId,
- PVOID pMsgBuffer )
- {
- // Try not to stay in this message handler for too long, otherwise
- // there will be a backlog of data. The best solution is to
- // queue data as it comes in, and then handle it on other threads
- // as this sample shows
-
- // This function is called by the DirectPlay message handler pool of
- // threads, so be care of thread synchronization problems with shared memory
-
- HRESULT hReturn = S_OK;
-
- switch( dwMessageId )
- {
- case DPN_MSGID_CREATE_PLAYER:
- {
- HRESULT hr;
- PDPNMSG_CREATE_PLAYER pCreatePlayerMsg;
- pCreatePlayerMsg = (PDPNMSG_CREATE_PLAYER) pMsgBuffer;
-
- APP_PLAYER_INFO* pPlayerInfo;
- if( FAILED( hr = LinkPlayer( pCreatePlayerMsg->dpnidPlayer,
- &pPlayerInfo ) ) )
- return DXTRACE_ERR( TEXT("LinkPlayer"), hr );
-
- // Tell DirectPlay to store this pPlayerInfo
- // pointer in the pvPlayerContext.
- pCreatePlayerMsg->pvPlayerContext = pPlayerInfo;
-
- // Post a message to the dialog thread to update the
- // UI. This keeps the DirectPlay message handler
- // from blocking
- if( g_hDlg != NULL )
- PostMessage( g_hDlg, WM_APP_UPDATE_TARGETS, 0, 0 );
- break;
- }
-
- case DPN_MSGID_DESTROY_PLAYER:
- {
- PDPNMSG_DESTROY_PLAYER pDestroyPlayerMsg;
- pDestroyPlayerMsg = (PDPNMSG_DESTROY_PLAYER)pMsgBuffer;
- APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pDestroyPlayerMsg->pvPlayerContext;
-
- // Enter the player list CS, because we are about to
- // modify shared memory, and the data may be changed
- // concurrently from other DirectPlay message handler threads
- EnterCriticalSection( &g_csPlayerList );
-
- // Unlink pPlayerInfo from the chain, m_PlayerHead.
- pPlayerInfo->pPrev->pNext = pPlayerInfo->pNext;
- pPlayerInfo->pNext->pPrev = pPlayerInfo->pPrev;
-
- LeaveCriticalSection( &g_csPlayerList );
-
- // Stop sending if the target was destroyed
- if( g_pTargetPlayer )
- {
- if( g_pTargetPlayer->dpnidPlayer == pPlayerInfo->dpnidPlayer )
- g_bSendingData = FALSE;
- }
-
- PLAYER_LOCK(); // enter player context CS
- PLAYER_RELEASE( pPlayerInfo ); // Release player and cleanup if needed
- PLAYER_UNLOCK(); // leave player context CS
-
- // Post a message to the dialog thread to update the
- // UI. This keeps the DirectPlay message handler
- // from blocking
- if( g_hDlg != NULL )
- PostMessage( g_hDlg, WM_APP_UPDATE_TARGETS, 0, 0 );
- break;
- }
-
- case DPN_MSGID_TERMINATE_SESSION:
- {
- PDPNMSG_TERMINATE_SESSION pTerminateSessionMsg;
- pTerminateSessionMsg = (PDPNMSG_TERMINATE_SESSION)pMsgBuffer;
-
- g_hrDialog = DPNERR_CONNECTIONLOST;
- EndDialog( g_hDlg, 0 );
- break;
- }
-
- case DPN_MSGID_HOST_MIGRATE:
- {
- PDPNMSG_HOST_MIGRATE pHostMigrateMsg;
- pHostMigrateMsg = (PDPNMSG_HOST_MIGRATE)pMsgBuffer;
-
- if( pHostMigrateMsg->dpnidNewHost == g_dpnidLocalPlayer )
- SetWindowText( g_hDlg, TEXT("DataRelay (Host)") );
- break;
- }
-
- case DPN_MSGID_RECEIVE:
- {
- PDPNMSG_RECEIVE pReceiveMsg;
- pReceiveMsg = (PDPNMSG_RECEIVE)pMsgBuffer;
- APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pReceiveMsg->pvPlayerContext;
- if( NULL == pPlayerInfo )
- break;
-
- GAMEMSG_GENERIC* pMsg = (GAMEMSG_GENERIC*) pReceiveMsg->pReceiveData;
- if( pMsg->dwType == GAME_MSGID_GAMEPACKET )
- {
- GAMEMSG_GENERIC* pDataMsg = (GAMEMSG_GENERIC*) pMsg;
-
- // Make a new GAMEMSG_DATA_NODE and hand it off to the
- // app worker thread. It will process the node, and
- // then update the UI to show that the packet was processed
- GAMEMSG_DATA_NODE* pDataMsgNode = new GAMEMSG_DATA_NODE;
- ZeroMemory( pDataMsgNode, sizeof(GAMEMSG_DATA_NODE) );
-
- pDataMsgNode->dwType = DATA_TYPE_NETPACKET_RECIEVE;
- pDataMsgNode->dwPacketId = pDataMsg->dwPacketId;
- pDataMsgNode->pDataMsg = pDataMsg;
- pDataMsgNode->dwReceiveDataSize = pReceiveMsg->dwReceiveDataSize;
- pDataMsgNode->pPlayerFrom = pPlayerInfo;
- pDataMsgNode->hBufferHandle = pReceiveMsg->hBufferHandle;
-
- // Enter the data list CS, because we are about to modify shared memory, and
- // the data may be changed concurrently from other DirectPlay
- // message handler threads, as well as the the app worker thread
- EnterCriticalSection( &g_csDataList );
-
- g_dwDataRecieved += pReceiveMsg->dwReceiveDataSize;
-
- // Then add it to the circular linked list, g_DataHead
- // so it can be processed by a worker thread
- pDataMsgNode->pNext = &g_DataHead;
- pDataMsgNode->pPrev = g_DataHead.pPrev;
- g_DataHead.pPrev->pNext = pDataMsgNode;
- g_DataHead.pPrev = pDataMsgNode;
-
- LeaveCriticalSection( &g_csDataList );
-
- // Tell the app worker thread that there
- // is new network data availible
- SetEvent( g_hDPDataAvailEvent );
-
- // Tell DirectPlay to assume that ownership of the buffer
- // has been transferred to the application, and so it will
- // neither free nor modify it until ownership is returned
- // to DirectPlay through the ReturnBuffer() call.
- hReturn = DPNSUCCESS_PENDING;
- }
- break;
- }
-
- case DPN_MSGID_SEND_COMPLETE:
- {
- PDPNMSG_SEND_COMPLETE pSendCompleteMsg;
- pSendCompleteMsg = (PDPNMSG_SEND_COMPLETE)pMsgBuffer;
-
- GAMEMSG_GENERIC* pGameMsg;
- pGameMsg = (GAMEMSG_GENERIC*) pSendCompleteMsg->pvUserContext;
-
- if( pSendCompleteMsg->hResultCode == DPNERR_TIMEDOUT )
- {
- // Make a new GAMEMSG_DATA_NODE and hand it off to the
- // app worker thread. It will process the node, and
- // then update the UI to show that the packet timed out
- GAMEMSG_DATA_NODE* pDataMsgNode = new GAMEMSG_DATA_NODE;
- ZeroMemory( pDataMsgNode, sizeof(GAMEMSG_DATA_NODE) );
-
- pDataMsgNode->dwType = DATA_TYPE_NETPACKET_TIMEOUT;
- pDataMsgNode->dwPacketId = pGameMsg->dwPacketId;
-
- // Enter the data list CS, because we are about to modify shared memory, and
- // the data may be changed concurrently from other DirectPlay
- // message handler threads, as well as the the app worker thread
- EnterCriticalSection( &g_csDataList );
-
- // Then add it to the circular linked list, g_DataHead
- // so it can be processed by a worker thread
- pDataMsgNode->pNext = &g_DataHead;
- pDataMsgNode->pPrev = g_DataHead.pPrev;
- g_DataHead.pPrev->pNext = pDataMsgNode;
- g_DataHead.pPrev = pDataMsgNode;
-
- LeaveCriticalSection( &g_csDataList );
-
- // Tell the app worker thread that there
- // is new data availible.
- SetEvent( g_hDPDataAvailEvent );
- }
-
- SAFE_DELETE( pGameMsg );
-
- break;
- }
- }
-
- // Make sure the DirectPlay MessageHandler calls the CNetConnectWizard handler,
- // so it can be informed of messages such as DPN_MSGID_ENUM_HOSTS_RESPONSE.
- if( DPNSUCCESS_PENDING != hReturn && SUCCEEDED(hReturn) && g_pNetConnectWizard )
- hReturn = g_pNetConnectWizard->MessageHandler( pvUserContext, dwMessageId, pMsgBuffer );
-
- return hReturn;
- }
-
-
-
-
- //-----------------------------------------------------------------------------
- // Name: LinkPlayer()
- // Desc: Link a new player to the circular linked list, g_PlayerHead. This
- // is called by the DirectPlay message handler threads, so we enter a
- // CS to avoid thread syncoronization problems.
- //-----------------------------------------------------------------------------
- HRESULT LinkPlayer( DPNID dpnid, APP_PLAYER_INFO** ppPlayerInfo )
- {
- HRESULT hr;
-
- // Create a new and fill in a APP_PLAYER_INFO
- APP_PLAYER_INFO* pPlayerInfo = new APP_PLAYER_INFO;
- pPlayerInfo->dpnidPlayer = dpnid;
- pPlayerInfo->lRefCount = 1;
-
- // Get the peer info and extract its name
- DWORD dwSize = 0;
- DPN_PLAYER_INFO* pdpPlayerInfo = NULL;
- hr = DPNERR_CONNECTING;
-
- // GetPeerInfo might return DPNERR_CONNECTING when connecting,
- // so just keep calling it if it does
- while( hr == DPNERR_CONNECTING )
- hr = g_pDP->GetPeerInfo( dpnid, pdpPlayerInfo, &dwSize, 0 );
-
- if( hr == DPNERR_BUFFERTOOSMALL )
- {
- pdpPlayerInfo = (DPN_PLAYER_INFO*) new BYTE[ dwSize ];
- ZeroMemory( pdpPlayerInfo, dwSize );
- pdpPlayerInfo->dwSize = sizeof(DPN_PLAYER_INFO);
-
- hr = g_pDP->GetPeerInfo( dpnid, pdpPlayerInfo, &dwSize, 0 );
- if( SUCCEEDED(hr) )
- {
- // This stores a extra TCHAR copy of the player name for
- // easier access. This will be redundent copy since DPlay
- // also keeps a copy of the player name in GetPeerInfo()
- DXUtil_ConvertWideStringToGeneric( pPlayerInfo->strPlayerName,
- pdpPlayerInfo->pwszName, MAX_PLAYER_NAME );
-
- pPlayerInfo->dwFlags = pdpPlayerInfo->dwPlayerFlags;
-
- if( pdpPlayerInfo->dwPlayerFlags & DPNPLAYER_LOCAL )
- g_dpnidLocalPlayer = dpnid;
- }
-
- SAFE_DELETE_ARRAY( pdpPlayerInfo );
- }
-
- // Enter the player list CS, because we are about to modify shared memory, and
- // the data may be changed concurrently from the DirectPlay
- // message handler threads
- EnterCriticalSection( &g_csPlayerList );
-
- // Then add it to the circular linked list, g_PlayerHead,
- pPlayerInfo->pNext = g_PlayerHead.pNext;
- pPlayerInfo->pPrev = &g_PlayerHead;
- g_PlayerHead.pNext->pPrev = pPlayerInfo;
- g_PlayerHead.pNext = pPlayerInfo;
-
- LeaveCriticalSection( &g_csPlayerList );
-
- if( ppPlayerInfo )
- *ppPlayerInfo = pPlayerInfo;
-
- return S_OK;
- }
-
-
-
-
-
-
-
-
- //-----------------------------------------------------------------------------
- // Name: DirectPlayLobbyMessageHandler
- // Desc: Handler for DirectPlay lobby messages. This function is called by
- // the DirectPlay lobby message handler pool of threads, so be careful of
- // thread synchronization problems with shared memory
- //-----------------------------------------------------------------------------
- HRESULT WINAPI DirectPlayLobbyMessageHandler( PVOID pvUserContext,
- DWORD dwMessageId,
- PVOID pMsgBuffer )
- {
- switch( dwMessageId )
- {
- case DPL_MSGID_CONNECT:
- {
- PDPL_MESSAGE_CONNECT pConnectMsg;
- pConnectMsg = (PDPL_MESSAGE_CONNECT)pMsgBuffer;
-
- // The CNetConnectWizard will handle this message for us,
- // so there is nothing we need to do here for this simple
- // sample.
- break;
- }
-
- case DPL_MSGID_DISCONNECT:
- {
- PDPL_MESSAGE_DISCONNECT pDisconnectMsg;
- pDisconnectMsg = (PDPL_MESSAGE_DISCONNECT)pMsgBuffer;
-
- // We should free any data associated with the lobby
- // client here, but there is none.
- break;
- }
-
- case DPL_MSGID_RECEIVE:
- {
- PDPL_MESSAGE_RECEIVE pReceiveMsg;
- pReceiveMsg = (PDPL_MESSAGE_RECEIVE)pMsgBuffer;
-
- // The lobby client sent us data. This sample doesn't
- // expected data from the client, but it is useful
- // for more complex apps.
- break;
- }
-
- case DPL_MSGID_CONNECTION_SETTINGS:
- {
- PDPL_MESSAGE_CONNECTION_SETTINGS pConnectionStatusMsg;
- pConnectionStatusMsg = (PDPL_MESSAGE_CONNECTION_SETTINGS)pMsgBuffer;
-
- // The lobby client has changed the connection settings.
- // This simple sample doesn't handle this, but more complex apps may
- // want to.
- break;
- }
- }
-
- // Make sure the DirectPlay MessageHandler calls the CNetConnectWizard handler,
- // so the wizard can be informed of lobby messages such as DPL_MSGID_CONNECT
- if( g_pNetConnectWizard )
- return g_pNetConnectWizard->LobbyMessageHandler( pvUserContext, dwMessageId,
- pMsgBuffer );
-
- return S_OK;
- }
-
-
-
-
- //-----------------------------------------------------------------------------
- // Name: SendNetworkData()
- // Desc: Sends a network packet of the specified size to the specified target.
- // This is called by the dialog UI thread.
- //-----------------------------------------------------------------------------
- HRESULT SendNetworkData()
- {
- HRESULT hr;
-
- if( !g_bSendingData ||
- IsDlgButtonChecked( g_hDlg, IDC_SEND_READY ) == BST_UNCHECKED )
- {
- KillTimer( g_hDlg, TIMERID_NETWORK );
- return S_OK;
- }
-
- if( g_pTargetPlayer == NULL )
- return E_FAIL;
-
- GAMEMSG_GENERIC* pGameMsg = NULL;
- DWORD dwBufferSize;
-
- // Create a game packet based on the selected choices
- switch( g_dwSendSize )
- {
- case 512:
- {
- GAMEMSG_DATA_512* pMsg512 = new GAMEMSG_DATA_512;
- FillMemory( pMsg512->pBuffer, sizeof(pMsg512->pBuffer), 1 );
- pGameMsg = pMsg512;
- dwBufferSize = sizeof(GAMEMSG_DATA_512);
- break;
- }
-
- case 256:
- {
- GAMEMSG_DATA_256* pMsg256 = new GAMEMSG_DATA_256;
- FillMemory( pMsg256->pBuffer, sizeof(pMsg256->pBuffer), 2 );
- pGameMsg = pMsg256;
- dwBufferSize = sizeof(GAMEMSG_DATA_256);
- break;
- }
-
- case 128:
- {
- GAMEMSG_DATA_128* pMsg128 = new GAMEMSG_DATA_128;
- FillMemory( pMsg128->pBuffer, sizeof(pMsg128->pBuffer), 3 );
- pGameMsg = pMsg128;
- dwBufferSize = sizeof(GAMEMSG_DATA_128);
- break;
- }
-
- case 64:
- {
- GAMEMSG_DATA_64* pMsg64 = new GAMEMSG_DATA_64;
- FillMemory( pMsg64->pBuffer, sizeof(pMsg64->pBuffer), 4 );
- pGameMsg = pMsg64;
- dwBufferSize = sizeof(GAMEMSG_DATA_64);
- break;
- }
-
- case 32:
- {
- GAMEMSG_DATA_32* pMsg32 = new GAMEMSG_DATA_32;
- FillMemory( pMsg32->pBuffer, sizeof(pMsg32->pBuffer), 5 );
- pGameMsg = pMsg32;
- dwBufferSize = sizeof(GAMEMSG_DATA_32);
- break;
- }
-
- default:
- case 16:
- {
- GAMEMSG_DATA_16* pMsg16 = new GAMEMSG_DATA_16;
- FillMemory( pMsg16->pBuffer, sizeof(pMsg16->pBuffer), 6 );
- pGameMsg = pMsg16;
- dwBufferSize = sizeof(GAMEMSG_DATA_16);
- break;
- }
- }
-
- // Update the rest of the game msg
- g_dwPacketId++;
- pGameMsg->dwPacketId = g_dwPacketId;
- pGameMsg->dwType = GAME_MSGID_GAMEPACKET;
-
- // Make a new GAMEMSG_DATA_NODE and hand it off to the
- // app worker thread. It will process the node, and
- // then update the UI to show that a packet was sent
- GAMEMSG_DATA_NODE* pDataMsgNode = new GAMEMSG_DATA_NODE;
- ZeroMemory( pDataMsgNode, sizeof(GAMEMSG_DATA_NODE) );
-
- pDataMsgNode->dwType = DATA_TYPE_NETPACKET_SENT;
- pDataMsgNode->dwPacketId = pGameMsg->dwPacketId;
- pDataMsgNode->dwReceiveDataSize = dwBufferSize;
-
- // Enter the data list CS, because we are about to modify shared memory, and
- // the data may be changed concurrently from other DirectPlay
- // message handler threads, as well as the the app worker thread
- EnterCriticalSection( &g_csDataList );
-
- // Then add it to the circular linked list, g_DataHead
- // so it can be processed by a worker thread
- pDataMsgNode->pNext = &g_DataHead;
- pDataMsgNode->pPrev = g_DataHead.pPrev;
- g_DataHead.pPrev->pNext = pDataMsgNode;
- g_DataHead.pPrev = pDataMsgNode;
-
- LeaveCriticalSection( &g_csDataList );
-
- // This var is only accessed by the dialog
- // thread, so it is safe to access it directly
- g_dwDataSent += dwBufferSize;
-
- DPN_BUFFER_DESC bufferDesc;
- bufferDesc.dwBufferSize = dwBufferSize;
- bufferDesc.pBufferData = (BYTE*) pGameMsg;
-
- DPNHANDLE hAsync;
- hr = g_pDP->SendTo( g_pTargetPlayer->dpnidPlayer, &bufferDesc, 1,
- g_dwSendTimeout, pGameMsg, &hAsync,
- DPNSEND_NOLOOPBACK | DPNSEND_NOCOPY );
- // Ignore all errors except DPNERR_INVALIDPLAYER
- if( hr == DPNERR_INVALIDPLAYER )
- {
- // Stop sending if the target left game
- g_bSendingData = FALSE;
- PostMessage( g_hDlg, WM_APP_UPDATE_TARGETS, 0, 0 );
- return S_OK;
- }
-
-
- // Tell the app worker thread that there
- // is new data availible.
- SetEvent( g_hDPDataAvailEvent );
-
- return S_OK;
- }
-
-
-
-
- //-----------------------------------------------------------------------------
- // Name: ProcessNetDataProc()
- // Desc: Worker thread that processes data found in g_DataHead,
- // and updates the UI state based on the data
- //-----------------------------------------------------------------------------
- UINT WINAPI ProcessNetDataProc( LPVOID lpParameter )
- {
- HWND hDlg = (HWND) lpParameter;
- DWORD dwResult;
- BOOL bDone = FALSE;
- HANDLE ahHandles[2];
-
- ahHandles[0] = g_hDPDataAvailEvent;
- ahHandles[1] = g_hShutdownEvent;
-
- while( !bDone )
- {
- dwResult = WaitForMultipleObjects( 2, ahHandles, FALSE, INFINITE );
-
- switch( dwResult )
- {
- case WAIT_OBJECT_0 + 0:
- {
- // g_hDPDataAvailEvent is signaled, so there is
- // network data available to process
- if( FAILED( g_hrDialog = ProcessData() ) )
- {
- DXTRACE_ERR( TEXT("ProcessData"), g_hrDialog );
- bDone = TRUE;
- }
- break;
- }
-
- case WAIT_OBJECT_0 + 1:
- {
- // g_hShutdownEvent is signaled, so shut down
- bDone = TRUE;
- break;
- }
- }
- }
-
- return 0;
- }
-
-
-
-
- //-----------------------------------------------------------------------------
- // Name: ProcessData()
- // Desc: This is called by the app worker thread to process the data found in
- // g_DataHead. The DirectPlay message handler threads add new nodes
- // upon events such as packet packet recieve/send or timeout.
- //-----------------------------------------------------------------------------
- HRESULT ProcessData()
- {
- TCHAR* strNewLogLine = NULL;
-
- // Enter the data list CS, because we are about to read shared memory, and
- // the data may be changed concurrently from the DirectPlay
- // message handler threads
- EnterCriticalSection( &g_csDataList );
-
- GAMEMSG_DATA_NODE* pDeleteNode;
- GAMEMSG_DATA_NODE* pNode = g_DataHead.pNext;
- while( pNode != &g_DataHead )
- {
- // Allocate a string on the heap, and fill it
- // with text about what happened.
- strNewLogLine = new TCHAR[MAX_PATH];
-
- switch( pNode->dwType )
- {
- case DATA_TYPE_NETPACKET_RECIEVE:
- wsprintf( strNewLogLine, TEXT("Got %s's #%d (%d bytes)\r\n"),
- pNode->pPlayerFrom->strPlayerName,
- pNode->dwPacketId,
- pNode->dwReceiveDataSize );
-
- // The app can do more process of the network
- // packet here here -- i.e. change the
- // state of the game. This simple sample just
- // updates the UI.
- break;
-
- case DATA_TYPE_NETPACKET_SENT:
- wsprintf( strNewLogLine, TEXT("Sending #%d to %s (%d bytes)\r\n"),
- pNode->dwPacketId,
- g_pTargetPlayer->strPlayerName,
- pNode->dwReceiveDataSize );
- break;
-
- case DATA_TYPE_NETPACKET_TIMEOUT:
- wsprintf( strNewLogLine, TEXT("Packet #%d timed out\r\n"),
- pNode->dwPacketId );
- break;
- }
-
- // Post a message to the dialog thread, so it can
- // take the string on the heap, and appead it to the log.
- // If the log is appended from this thread, then if the dialog
- // thread tries to enter the g_csDataList CS, then it will be
- // locked until this thread release it, and the
- // AppendTextToEditControl function relies on reponses from the
- // dialog thread, so a race condition is possible unless
- // the dialog thread itself appeads the text.
- PostMessage( g_hDlg, WM_APP_APPEND_TEXT, (WPARAM) strNewLogLine, 0 );
-
- // The dialog thread will free the string
- strNewLogLine = NULL;
-
- if( pNode->hBufferHandle != NULL )
- {
- // Done with the buffer, so return it DirectPlay,
- // so that the memory can be reused
- g_pDP->ReturnBuffer( pNode->hBufferHandle,0 );
- }
-
- // Unlink pPlayerInfo from the chain, g_DataHead.
- pNode->pPrev->pNext = pNode->pNext;
- pNode->pNext->pPrev = pNode->pPrev;
-
- pDeleteNode = pNode;
- pNode = pNode->pNext;
-
- assert( pDeleteNode != &g_DataHead );
- SAFE_DELETE( pDeleteNode );
- }
-
- LeaveCriticalSection( &g_csDataList );
-
- return S_OK;
- }
-
-
-
-
- //-----------------------------------------------------------------------------
- // Name: AppendTextToEditControl()
- // Desc: Appends a string of text to the edit control
- //-----------------------------------------------------------------------------
- VOID AppendTextToEditControl( HWND hDlg, TCHAR* strNewLogLine )
- {
- static TCHAR strText[1024*10];
-
- HWND hEdit = GetDlgItem( hDlg, IDC_LOG_EDIT );
- SendMessage( hEdit, WM_SETREDRAW, FALSE, 0 );
- GetWindowText( hEdit, strText, 1024*9 );
-
- _tcscat( strText, strNewLogLine );
-
- int nSecondLine = 0;
- if( SendMessage( hEdit, EM_GETLINECOUNT, 0, 0 ) > 18 )
- nSecondLine = (int)SendMessage( hEdit, EM_LINEINDEX, 1, 0 );
-
- SetWindowText( hEdit, &strText[nSecondLine] );
-
- SendMessage( hEdit, WM_SETREDRAW, TRUE, 0 );
- InvalidateRect( hEdit, NULL, TRUE );
- UpdateWindow( hEdit );
- }
-
-
-
-
- //-----------------------------------------------------------------------------
- // Name: UpdateConnectionInfo()
- // Desc: Using the info buffer passed in, display some interesting tidbits about
- // the connection.
- //-----------------------------------------------------------------------------
- VOID UpdateConnectionInfo( HWND hDlg, PDPN_CONNECTION_INFO pConnectionInfo,
- DWORD dwHighMessages, DWORD dwHighBytes,
- DWORD dwNormalMessages, DWORD dwNormalBytes,
- DWORD dwLowMessages, DWORD dwLowBytes )
- {
- TCHAR strText[1024];
- int nDrops;
- int nSends;
-
- HWND hEdit = GetDlgItem( hDlg, IDC_CONNINFO_EDIT );
- int nThumbIndex = GetScrollPos( hEdit, SB_VERT );
-
- if( pConnectionInfo )
- {
- nDrops = pConnectionInfo->dwPacketsDropped + pConnectionInfo->dwPacketsRetried;
- nDrops *= 10000;
-
- nSends = pConnectionInfo->dwPacketsSentGuaranteed + pConnectionInfo->dwPacketsSentNonGuaranteed;
-
- if(nSends)
- nDrops /= nSends; // percent nDrops * 100
-
- _stprintf(strText,
- TEXT("Send Queue Messages High Priority=%d\r\n") \
- TEXT("Send Queue Bytes High Priority=%d\r\n") \
-
- TEXT("Send Queue Messages Normal Priority=%d\r\n") \
- TEXT("Send Queue Bytes Normal Priority=%d\r\n") \
-
- TEXT("Send Queue Messages Low Priority=%d\r\n") \
- TEXT("Send Queue Bytes Low Priority=%d\r\n") \
-
- TEXT("Round Trip Latency MS=%dms\r\n") \
- TEXT("Throughput BPS=%d\r\n") \
- TEXT("Peak Throughput BPS=%d\r\n") \
- \
- TEXT("Bytes Sent Guaranteed=%d\r\n") \
- TEXT("Packets Sent Guaranteed=%d\r\n") \
- TEXT("Bytes Sent Non-Guaranteed=%d\r\n") \
- TEXT("Packets Sent Non-Guaranteed=%d\r\n") \
- \
- TEXT("Bytes Retried Guaranteed=%d\r\n") \
- TEXT("Packets Retried Guaranteed=%d\r\n") \
- TEXT("Bytes Dropped Non-Guaranteed=%d\r\n") \
- TEXT("Packets Dropped Non-Guaranteed=%d\r\n") \
- \
- TEXT("Messages Transmitted High Priority=%d\r\n") \
- TEXT("Messages Timed Out High Priority=%d\r\n") \
- TEXT("Messages Transmitted Normal Priority=%d\r\n") \
- TEXT("Messages Timed Out Normal Priority=%d\r\n") \
- TEXT("Messages Transmitted Low Priority=%d\r\n") \
- TEXT("Messages Timed Out Low Priority=%d\r\n") \
- \
- TEXT("Bytes Received Guaranteed=%d\r\n") \
- TEXT("Packets Received Guaranteed=%d\r\n") \
- TEXT("Bytes Received Non-Guaranteed=%d\r\n") \
- TEXT("Packets Received Non-Guaranteed=%d\r\n") \
- TEXT("Messages Received=%d\r\n") \
- \
- TEXT("Loss Rate=%d.%02d%%\r\n"),
-
- dwHighMessages, dwHighBytes,
- dwNormalMessages, dwNormalBytes,
- dwLowMessages, dwLowBytes,
-
- pConnectionInfo->dwRoundTripLatencyMS,
- pConnectionInfo->dwThroughputBPS,
- pConnectionInfo->dwPeakThroughputBPS,
-
- pConnectionInfo->dwBytesSentGuaranteed,
- pConnectionInfo->dwPacketsSentGuaranteed,
- pConnectionInfo->dwBytesSentNonGuaranteed,
- pConnectionInfo->dwPacketsSentNonGuaranteed,
-
- pConnectionInfo->dwBytesRetried,
- pConnectionInfo->dwPacketsRetried,
- pConnectionInfo->dwBytesDropped,
- pConnectionInfo->dwPacketsDropped,
-
- pConnectionInfo->dwMessagesTransmittedHighPriority,
- pConnectionInfo->dwMessagesTimedOutHighPriority,
- pConnectionInfo->dwMessagesTransmittedNormalPriority,
- pConnectionInfo->dwMessagesTimedOutNormalPriority,
- pConnectionInfo->dwMessagesTransmittedLowPriority,
- pConnectionInfo->dwMessagesTimedOutLowPriority,
-
- pConnectionInfo->dwBytesReceivedGuaranteed,
- pConnectionInfo->dwPacketsReceivedGuaranteed,
- pConnectionInfo->dwBytesReceivedNonGuaranteed,
- pConnectionInfo->dwPacketsReceivedNonGuaranteed,
- pConnectionInfo->dwMessagesReceived,
-
- (nDrops/100), (nDrops % 100) );
- }
- else
- {
- strText[0] = 0;
- }
-
- SetWindowText( hEdit, strText );
- SendMessage( hEdit, EM_LINESCROLL, 0, nThumbIndex );
- }
-
-
-
-
- //-----------------------------------------------------------------------------
- // Name: UpdateStats()
- // Desc:
- //-----------------------------------------------------------------------------
- VOID UpdateStats( HWND hDlg )
- {
- HRESULT hr;
- static DWORD s_dwLastTime = timeGetTime();
- DWORD dwCurTime = timeGetTime();
-
- if( (dwCurTime - s_dwLastTime) < 200 )
- return;
-
- TCHAR strDataIn[32];
- TCHAR strDataOut[32];
-
- FLOAT fSecondsPassed = (dwCurTime - s_dwLastTime) / 1000.0f;
- FLOAT fDataIn = g_dwDataRecieved / fSecondsPassed;
- FLOAT fDataOut = g_dwDataSent / fSecondsPassed;
-
- s_dwLastTime = dwCurTime;
- g_dwDataRecieved = 0;
- g_dwDataSent = 0;
-
- _stprintf( strDataIn, TEXT("%0.1f BPS"), fDataIn );
- _stprintf( strDataOut, TEXT("%0.1f BPS"), fDataOut );
- SetDlgItemText( hDlg, IDC_ACTUAL_SEND_RATE, strDataOut );
- SetDlgItemText( hDlg, IDC_ACTUAL_RECIEVE_RATE, strDataIn );
-
- // If a player has been selected for Connection Info Display we will handle it next
- if( g_pConnInfoTargetPlayer->dpnidPlayer != 0 )
- {
- // Call GetConnectionInfo and display results
- DPN_CONNECTION_INFO dpnConnectionInfo;
- ZeroMemory( &dpnConnectionInfo, sizeof(DPN_CONNECTION_INFO) );
- dpnConnectionInfo.dwSize = sizeof(DPN_CONNECTION_INFO);
- hr = g_pDP->GetConnectionInfo( g_pConnInfoTargetPlayer->dpnidPlayer,
- &dpnConnectionInfo,
- 0);
-
- if( SUCCEEDED(hr) )
- {
- DWORD dwHighMessages, dwHighBytes;
- DWORD dwNormalMessages, dwNormalBytes;
- DWORD dwLowMessages, dwLowBytes;
-
- hr = g_pDP->GetSendQueueInfo( g_pConnInfoTargetPlayer->dpnidPlayer,
- &dwHighMessages, &dwHighBytes,
- DPNGETSENDQUEUEINFO_PRIORITY_HIGH );
- if( FAILED(hr) )
- DXTRACE_ERR( TEXT("GetSendQueueInfo"), hr );
-
- hr = g_pDP->GetSendQueueInfo( g_pConnInfoTargetPlayer->dpnidPlayer,
- &dwNormalMessages, &dwNormalBytes,
- DPNGETSENDQUEUEINFO_PRIORITY_NORMAL );
- if( FAILED(hr) )
- DXTRACE_ERR( TEXT("GetSendQueueInfo"), hr );
-
- hr = g_pDP->GetSendQueueInfo( g_pConnInfoTargetPlayer->dpnidPlayer,
- &dwLowMessages, &dwLowBytes,
- DPNGETSENDQUEUEINFO_PRIORITY_LOW );
- if( FAILED(hr) )
- DXTRACE_ERR( TEXT("GetSendQueueInfo"), hr );
-
- UpdateConnectionInfo( hDlg, &dpnConnectionInfo,
- dwHighMessages, dwHighBytes,
- dwNormalMessages, dwNormalBytes,
- dwLowMessages, dwLowBytes );
- }
- else
- {
- // If the player goes away, the set the target to none
- SendDlgItemMessage( hDlg, IDC_CONNINFO_COMBO, CB_SETCURSEL, 0, 0 );
- ReadCombos( hDlg );
- }
- }
- else
- {
- // Clear the conn info window if no connection is selected to display
- UpdateConnectionInfo( hDlg, NULL, 0, 0, 0, 0, 0, 0 );
- }
- }
-
-
-